/* 
 * Harddisk protection for HP notebooks
 * Author: Erwin Walraven
 *
 * Improved version of the original code,
 * written by Eric Piel and Pavel Machek.
 * 
 * License: GPL2
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <sched.h>
#include <errno.h>

#define UNLOAD           "/sys/block/sda/device/unload_heads"
#define LED              "/sys/class/leds/hp::hddprotect/brightness"
#define ACCELEROMETER    "/dev/freefall"
#define AC_POWER         "/sys/class/power_supply/AC/online"

void write_file(char *path, int val)
{
	FILE *fp;
	char buf[1024];

	sprintf(buf, "%d", val);

	if(fp = fopen(path, "w")){
		fputs(buf, fp);
		fclose(fp);
	}else{
		perror(path);
		exit(1);
	}
}

void set_protection_led(char val)
{
	write_file(LED, val);
}

void unload_heads(char sec)
{
	write_file(UNLOAD, sec*1000);
}

int on_ac()
{
	FILE *fp;
	int ret = 0;

	if(fp = fopen(AC_POWER, "r")){
		fscanf(fp, "%d", &ret);
		fclose(fp);
	}else{
		perror(AC_POWER);
		exit(1);
	}

	return ret;
}

void protect(char sec)
{
	alarm(0);
	set_protection_led(1);
	unload_heads(sec+1);
	alarm(sec);
}

void reset()
{
	set_protection_led(0);
	unload_heads(0);
}

int main()
{
	struct sched_param param;
	FILE *fp;
	unsigned char count;
	int ret;

	if(!(fp = fopen(ACCELEROMETER, "r"))){
		perror(ACCELEROMETER);
		return 1;
	}

	signal(SIGALRM, reset);
	daemon(0, 0);
	mlockall(MCL_CURRENT | MCL_FUTURE);
	param.sched_priority = sched_get_priority_max(SCHED_FIFO);
	sched_setscheduler(0, SCHED_FIFO, &param); 

	while(1){
		ret = fread(&count, sizeof(count), 1, fp);    //blocks till freefall

		if(ret == -1 && errno == EINTR)
			continue;

		if(ret != sizeof(count)){
			perror("read");
			break;
		}
		
		protect(on_ac() ? 3 : 6);
	}

	fclose(fp);
	return 0;
}
